<?php
/**
 * 商品表
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2018/6/7
 * @copyright PanxSoft Inc.
 */

namespace App\Models\Panx;

use App\Http\Controllers\Common\YouzanOrderController;
use App\Models\Admin\CosmeticMaterial;
use App\Models\Concerns\ScopeFilterable;
use App\Models\Customer\Asset;
use App\Models\Customer\Customer;
use App\Models\Customer\DeviceAccount;
use App\Models\Store\Store;
use App\Models\Store\StorePivotCustomerProduct;
use App\Services\Locale\Language;
use App\Models\Customer\Language as ModelLanguage;
use App\Services\Utils\Guard;
use App\Services\Utils\UTCTime;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Support\Facades\DB;

/**
 * @property int id
 * @property int try_on - 试戴次数
 * @property string content - 富文本内容
 * @property string product_url
 * @property string title
 * @property string name
 * @property string description
 * @property string spu_code
 * @property int stock
 * @property int sex
 * @property int is_sale
 * @property int is_deleted
 * @property int ref_count
 * @property int src_product_id
 * @property int cosmetic_material_id
 * @property int category_id
 * @property int brand_id - 品牌 ID
 * @property int asset_id - 素材 ID
 * @property int $youzan_item_id - 有赞商品ID
 * @property int product_url_type_id
 * @property int brand_series_id
 * @property string customer_id
 * @property string store_id
 * @property Carbon created_at
 * @property Carbon updated_at
 * @property int is_hot - 未使用
 * @property int is_new - 未使用
 *
 * @property int|null new_brand_id - 指定共享产品复制到的新品牌
 * @property int|null new_brand_series_id - 指定共享产品复制到的新的品牌系列
 *
 * @property-read string market_price
 * @property-read string sell_price
 * @property-read string category_name
 * @property-read string brand_logo
 * @property-read string brand_name
 * @property-read array|string[] tag_names - 标签名称数组
 * @property-read string|null $asset_code - 关联素材的编码
 * @property-read int|null $asset_version - 关联素材的版本号
 * @property-read array|string[] body_position_names
 * @property-read string|null thumb_url
 * @property-read string|null color_thumb
 * @property-read string|null color_thumb_url
 *
 * @property-read ProductImage|null thumbImage
 * @property-read ProductImage|null colorThumbImage
 * @property-read Brand brand
 * @property-read Category category
 * @property-read Asset|null asset
 * @property-read BrandSeries brandSeries
 * @property-read ProductUrlType|null productUrlType
 * @property-read ProductUrlType|null link_product
 * @property-read CosmeticMaterial cosmeticMaterial
 * @property-read BodyPosition[]|Collection bodyPositions
 * @property-read Collection|Product[] shareProducts
 * @property-read null|Product sourceProduct
 * @property-read ProductTag[]|Collection productTags
 * @property-read ProductSku[]|Collection productSkus
 * @property-read SpecValue[]|Collection specValues
 * @property-read AttributeValue[]|Collection attributeValues
 * @property-read ProductImage[]|Collection productImages
 * @property-read string|null asset_xml
 * @property-read Customer|null customer
 * @property-read Store|null store
 *
 * @property-write int customer_ref_count - 对于虚拟供应商的共享商品，被特定的商家引用的次数
 * @property-write int product_type - 产品类型（化妆/护肤）
 * @property-write string sex_name - 性别名称
 * @property-write string product_tag_names - tag names 数组字符串拼接
 * @property-write bool is_ref_asset
 * @property-write null|string product_thumb_url
 *
 * @method static self filterCustomerId(int $customer_id)
 * @method static self filterCustomersOwn(int[] $customer_id)
 * @method static self filterStoreId($store_id)
 * @method static self filterStoreMixCustomer(Store $store_id)
 * @method static self filterStoreProductType($store, ?string $product_type)
 *
 * @method static self filterIsSale(?int $is_sale)
 * @method static self filterIsDeleted(?int $is_deleted)
 * @method static self filterIsHot(?int $is_hot)
 * @method static self filterIsNew(?int $is_new)
 * @method static self filterHasYouzan(?string $if_youzan)
 *
 * @method static self filterBrand(int $brand_id)
 * @method static self filterBrandSeries(int $brand_series_id)
 * @method static self filterBrandName(string $brand_name)
 * @method static self filterCategory(?int $category_id)
 *
 * @method static self filterOnShelf()
 * @method static self filterStorePivotCustomerProductOnShelf($store_id)
 * @method static self filterStoreIsSale($store_id, $is_sale)
 * @method static self filterStoreIsDeleted($is_deleted)
 *
 * @method static self filterBodyPosition($body_position_id)
 * @method static self filterSex(?int $sex)
 *
 * @method static self filterCategoryHasAsset(?int $has_asset)
 *
 * @mixin Builder
 */
class Product extends Model
{
    use ScopeFilterable;
    use UTCTime;

    const IS_SALE = 1;    // 出售中
    const IS_NO_SALE = 0; // 下架了

    const IS_DELETE = 1;    // 已经删除
    const IS_NO_DELETE = 0; // 未删除

    const STORE_ID_CUSTOMER = 0; // 是客户上传的产品

    // 无产品模型 ID
    const NO_MOD_ID = 0;

    // 无 store 的默认ID
    const NO_STORE_ID = 0;

    // 无品牌 ID
    const NO_BRAND_ID = 0;

    // 无品牌系列 ID
    const NO_BRAND_SERIES_ID = 0;

    const STORE_CUSTOMER_PRODUCT = 'store_customer';
    const CUSTOMER_PRODUCT = 'customer';
    const STORE_PRODUCT = 'store';

    const YOUZAN_PRODUCT = 'youzan';
    const NOT_YOUZAN_PRODUCT = 'not_youzan';
    const NO_YOUZAN_ITEM_ID = 0; // 不是有赞的商品

    protected $table = 'products';

    protected $guarded = [];

    protected $casts = [
        'store_id'             => 'int',
        'customer_id'          => 'int',
        'category_id'          => 'int',
        'youzan_item_id'       => 'int',
        'brand_id'             => 'int',
        'product_mod_id'       => 'int',
        'cosmetic_material_id' => 'int',
        'src_product_id'       => 'int',
        'product_url_type_id'  => 'int',
        'asset_id'             => 'int',
        'sell_price'           => 'float',
        'market_price'         => 'float',
        'stock'                => 'float',
        'priority'             => 'int',
        'try_on'               => 'int',
        'ref_count'            => 'int',
    ];

    protected $appends = [
        'utc_created_at'
    ];


    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function brand()
    {
        return $this->belongsTo(Brand::class, 'brand_id', 'id');
    }

    /**
     * @used-by $cosmeticMaterial
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function cosmeticMaterial()
    {
        return $this->belongsTo(Cosmeticmaterial::class, 'cosmetic_material_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function categoryDescriptionByLanguage()
    {
        $language_code = app()->getLocale();
        $language_id = ModelLanguage::mapCodeToId($language_code);

        return $this->hasOne(CategoryDescription::class, 'category_id', 'category_id')
            ->where('language_id', $language_id);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function asset()
    {
        return $this->belongsTo(Asset::class, 'asset_id', 'id');
    }

    public function productTags()
    {
        return $this->belongsToMany(ProductTag::class, 'product_pivot_tag', 'product_id', 'product_tag_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function thumbImage()
    {
        return $this->hasOne(ProductImage::class)
            ->where('type', ProductImage::TYPE_THUMB);
    }

    public function brandSeries()
    {
        return $this->belongsTo(BrandSeries::class, 'brand_series_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function colorThumbImage()
    {
        return $this->hasOne(ProductImage::class)
            ->where('type', ProductImage::TYPE_COLOR_THUMB);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function productImages()
    {
        return $this->hasMany(ProductImage::class, 'product_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function bodyPositions()
    {
        return $this->belongsToMany(BodyPosition::class, 'product_pivot_body_position', 'product_id', 'body_position_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function attributeValues()
    {
        return $this->belongsToMany(AttributeValue::class, 'product_pivot_attribute_value', 'product_id', 'attribute_value_id')
            ->withPivot('product_id', 'attribute_value_id')
            ->withTimestamps();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function specValues()
    {
        return $this->belongsToMany(SpecValue::class, 'product_pivot_spec_value', 'product_id', 'spec_value_id')
            ->withPivot('product_id', 'spec_value_id', 'thumb')
            ->withTimestamps();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function productSkus()
    {
        return $this->hasMany(ProductSku::class, 'product_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function customers()
    {
        return $this->belongsToMany(Customer::class, 'customer_pivot_virtual_product', 'product_id', 'customer_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function store()
    {
        return $this->belongsTo(Store::class, 'store_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function customer()
    {
        return $this->belongsTo(Customer::class, 'customer_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function stores()
    {
        return $this->belongsToMany(Store::class, 'store_pivot_virtual_product', 'product_id', 'store_id');
    }

    /**
     * @used-by $productUrlType
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function productUrlType()
    {
        return $this->belongsTo(ProductUrlType::class, 'product_url_type_id', 'id');
    }

    /**
     * 通过 store_pivot_customer_product 表关联的店铺
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function storePivotCustomerProductStores()
    {
        return $this->belongsToMany(Store::class, 'store_pivot_customer_product', 'product_id', 'store_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function records()
    {
        return $this->morphMany(Record::class, 'objectable');
    }

    /**
     * @used-by $shareProducts - 引用此商品（共享）的复制出来的商品
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function shareProducts()
    {
        return $this->hasMany(Product::class, 'src_product_id', 'id');
    }

    /**
     * @used-by $sourceProduct - 该商品（复制）所引用的源商品
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function sourceProduct()
    {
        return $this->belongsTo(Product::class, 'src_product_id', 'id');
    }

    /**
     * @used-by filterCustomerId
     *
     * @param Builder $query
     * @param $customer_id
     * @return Builder
     */
    public function scopeFilterCustomerId(Builder $query, $customer_id)
    {
        if (!$customer_id) return $query;

        return $query->where('customer_id', $customer_id);
    }

    /**
     * @used-by filterIsSale()
     *
     * @param Builder $query
     * @param $is_sale
     * @return Builder
     */
    public function scopeFilterIsSale(Builder $query, ?int $is_sale)
    {
        if (!isset($is_sale)) return $query;

        return $query->where($this->qualifyColumn('is_sale'), $is_sale);
    }

    /**
     * @param Builder $query
     * @param $is_deleted
     * @return Builder
     */
    public function scopeFilterIsDeleted(Builder $query, $is_deleted)
    {
        if (!isset($is_deleted)) return $query;

        return $query->where('products.is_deleted', $is_deleted);
    }

    /**
     * @used-by filterStoreIsSale - 需要和 joinSelectStorePvtCustomerProduct 一起使用，比较耦合
     *
     * @param Builder $query
     * @param $store_id
     * @param $is_sale
     * @return Builder
     */
    public function scopeFilterStoreIsSale(Builder $query, $store_id, int $is_sale = null)
    {
        if (! isset($is_sale)) return $query;

        if ($is_sale === self::IS_SALE) {
            return $query->where([ // 店铺自己的商品(或 关联客户商品状态未设置 )，为上架
                ['products.is_sale', self::IS_SALE],
                ['store_pivot_customer_product.product_id', null],
            ])->orWhere([ // 关联客户商品状态已设置，关联表状态必须为上架
                ['products.is_sale', self::IS_SALE],
                ['store_pivot_customer_product.product_id', '<>', null],
                ['store_pivot_customer_product.is_sale', self::IS_SALE],
            ]);
        } else {
            return $query->where([ // 产品本身下架了
                ['products.is_sale', self::IS_NO_SALE],
            ])->orWhere([ // 或在关联表中设置了下架状态
                ['store_pivot_customer_product.is_sale', self::IS_NO_SALE],
            ]);
        }
    }

    /**
     * @used-by filterStoreIsDeleted
     *
     * @param Builder $query
     * @param $is_deleted
     * @return Builder
     */
    public function scopeFilterStoreIsDeleted(Builder $query, $is_deleted)
    {
        if (!isset($is_deleted)) return $query;

        return $query->where([
            ['products.is_deleted', $is_deleted],
            ['product_id', null],
        ])->orWhere([
            ['store_pivot_customer_product.is_deleted', $is_deleted],
        ]);
    }

    /**
     * @used-by filterBrand()
     *
     * @param Builder $query
     * @param $type_id
     * @return Builder
     */
    public function scopeFilterBrand(Builder $query, $type_id = null)
    {
        if (!isset($type_id)) return $query;

        return $query->where('brand_id', $type_id);
    }

    /**
     *  @used-by filterBrandSeries()
     *
     * @param Builder $query
     * @param $brand_series_id
     * @return Builder
     */
    public function scopeFilterBrandSeries(Builder $query, $brand_series_id)
    {
        if (!isset($brand_series_id)) return $query;

        return $query->where('brand_series_id', $brand_series_id);
    }

    /**
     * @used-by filterBrandName()
     *
     * @param Builder $query
     * @param $brand_name
     * @return Builder
     */
    public function scopeFilterBrandName(Builder $query, $brand_name)
    {
        if (!$brand_name || $brand_name === '全部') return $query;

        return $query->whereHas('brand', function (Builder $query) use ($brand_name) {
            return $query->where('name', 'like',  trim($brand_name));
        });
    }

    /**
     * @used-by filterCategory() - 过滤指定产品分类
     *
     * @param Builder $query
     * @param $type_id
     * @return Builder
     */
    public function scopeFilterCategory(Builder $query, ?int $type_id)
    {
        if (is_null($type_id) || $type_id === 0) return $query;

        return $query->where('category_id', $type_id);
    }

    /**
     * @param Builder $query
     * @param $is_hot
     * @return Builder
     */
    public function scopeFilterIsHot(Builder $query, $is_hot)
    {
        if (!isset($is_hot)) return $query;

        return $query->where('is_hot', $is_hot);
    }

    /**
     * @used-by filterHasYouzan()
     *
     * @param Builder $query
     * @param string|null $if_youzan
     *
     * @return Builder
     */
    public function scopeFilterHasYouzan(Builder $query, ?string $if_youzan)
    {
        switch ($if_youzan) {
            case Product::YOUZAN_PRODUCT: // 只显示有赞
                return $query->where('products.youzan_item_id', '<>', 0);
                break;
            case Product::NOT_YOUZAN_PRODUCT: // 不显示有赞商品
                return $query->where('products.youzan_item_id', 0);
                break;
            default :
                return $query;
                break;
        }
    }

    /**
     * @param Builder $query
     * @param $is_new
     * @return Builder
     */
    public function scopeFilterIsNew(Builder $query, $is_new)
    {
        if (!isset($is_new)) return $query;

        return $query->where('is_new', $is_new);
    }

    /**
     * @used-by filterStoreId()
     *
     * @param Builder $query
     * @param $store_id
     * @return Builder
     */
    public function scopeFilterStoreId(Builder $query, $store_id)
    {
        if (!$store_id) return $query;

        return $query->where($this->qualifyColumn('store_id'), $store_id);
    }

    /**
     * @used-by filterStoreProductType - 搜索店铺下查看 商家 / 店铺 / 商家+店铺 类型的产品
     *
     * @param Builder $query
     * @param $store
     * @param $product_type
     * @return Builder
     */
    public function scopeFilterStoreProductType(Builder $query, Store $store, ?string $product_type)
    {
        switch ($product_type) {
            // 同时显示 store & customer 未下架未删除的商品
            case Product::STORE_CUSTOMER_PRODUCT:
                return $query
                    ->where($this->qualifyColumn('store_id'), $store->id)
                    ->orWhereIn($this->getQualifiedKeyName(), $store->customer->selfIsSaleProductIds());

            // 只显示customer未下架未删除的商品
            case Product::CUSTOMER_PRODUCT:
                return $query->whereIn($this->getQualifiedKeyName(), $store->customer->selfIsSaleProductIds());

            // store|null  只显示store自己的商品
            case Product::STORE_PRODUCT:
            default :
                return $query->where($this->qualifyColumn('store_id'), $store->id);
        }

    }

    /**
     * @param $image
     * @return ProductImage|null
     * @throws \Exception
     */
    public function updateColorThumb(?UploadedFile $image): ?ProductImage
    {
        if ($image) {
            $this->deleteColorThumbImage();

            $thumb = (new ProductImage)->create([
                'product_id' => $this->id,
                'key'        => $image,
                'type'       => ProductImage::TYPE_COLOR_THUMB,
            ]);
        } else {
            $thumb = null;
        }

        return $thumb;
    }

    /**
     * @param $image
     * @return ProductImage|null
     * @throws \Exception
     */
    public function updateThumb(?UploadedFile $image): ?ProductImage
    {
        if ($image) {
            $this->deleteThumbImage();

            $thumb = ProductImage::create([
                'product_id' => $this->id,
                'key'        => $image,
                'type'       => ProductImage::TYPE_THUMB,
            ]);

            return $thumb;
        } else {
            return null;
        }
    }

    /**
     * @return bool
     * @throws \Exception
     */
    public function deleteThumbImage(): bool
    {
        $thumb = $this->thumbImage;

        return $thumb ? $thumb->delete() : false;
    }

    /**
     * @return bool
     * @throws \Exception
     */
    public function deleteColorThumbImage(): bool
    {
        $thumb = $this->colorThumbImage;

        return $thumb ? $thumb->delete() : false;
    }

    /**
     * @used-by thumb_url
     *
     * @return null|string
     */
    public function getThumbUrlAttribute(): ?string
    {
        return $this->thumbImage->url ?? null;
    }

    /**
     * @used-by color_thumb_url
     *
     * @return null|string
     */
    public function getColorThumbUrlAttribute()
    {
        return $this->colorThumbImage->url ?? null;
    }

    /**
     * @used-by color_thumb
     *
     * @return null|string
     */
    public function getColorThumbAttribute(): ?string
    {
        return optional($this->colorThumbImage)->url ?: optional($this->thumbImage)->url;
    }

    /**
     * @used-by asset_xml
     *
     * @return null|string
     */
    public function getAssetXmlAttribute(): ?string
    {
        return $this->asset->model_xml ?? null;
    }

    /**
     * @used-by $asset_code
     *
     * @return null|string
     */
    public function getAssetCodeAttribute(): ?string
    {
        return $this->asset->code ?? null;
    }

    /**
     * @used-by $asset_version
     *
     * @return int|null
     */
    public function getAssetVersionAttribute(): ?int
    {
        return $this->asset->version ?? null;
    }

    /**
     * @used-by $brand_series_name
     *
     * @return int|null
     */
    public function getBrandSeriesNameAttribute()
    {
        return $this->brandSeries->name ?? null;
    }

    /**
     * @used-by $cosmetic_material_name
     *
     * @return int|null
     */
    public function getCosmeticMaterialNameAttribute()
    {
        return $this->cosmeticMaterial->name ?? null;
    }

    /**
     * @used-by tag_names
     *
     * @return BaseCollection
     */
    public function getTagNamesAttribute(): BaseCollection
    {
        return $this->productTags->pluck(Language::getLocalNameOrConst());
    }

    /**
     * @used-by body_position_names
     *
     * @return array
     */
    public function getBodyPositionNamesAttribute()
    {
        return $this->bodyPositions()
            ->get([Language::getLocalNameColumn()])
            ->pluck('name')
            ->all();
    }

    /**
     * @used-by brand_logo
     *
     * @return null|string
     */
    public function getBrandLogoAttribute(): ?string
    {
        return $this->brand->logo ?? null;
    }

    /**
     * @used-by category_name
     *
     * @return null|string
     */
    public function getCategoryNameAttribute()
    {
        if (isset($this->relations['categoryDescriptionByLanguage'])) {
            return $this->categoryDescriptionByLanguage->name ?? null;
        }

        return CategoryDescription::mapIDtoName($this->category_id);
    }

    /**
     * @used-by brand_name
     *
     * @return mixed|null|string
     */
    public function getBrandNameAttribute()
    {
        if (isset($this->relations['brand'])) {
            return $this->brand->name ?? null;
        }

        return Brand::mapIDtoName($this->brand_id);
    }

    /**
     * @used-by link_product
     *
     * @return ProductUrlType|null
     */
    public function getLinkProductAttribute(): ?ProductUrlType
    {
        if (isset($this->relations['productUrlType'])) {
            $product_url_type = $this->productUrlType;
        } else {
            $product_url_type = ProductUrlType::findFromAllCache($this->product_url_type_id);
        }

        if ($product_url_type) {
            $product_url_type->url = $this->product_url;
        }

        return $product_url_type;
    }

    /**
     * @used-by $product_url
     *
     * @return int|null
     */
    public function getProductUrlAttribute()
    {
        $attr_product_url = $this->attributes['product_url'];
        if (Guard::hasAdmin()) { // 后台修改时，直接返回原地址
            return $attr_product_url;
        }

        if ($this->isSyncYouzanProductUrl()) {
            $url = route(YouzanOrderController::ROUTES['redirectUrl'], [
                'device_account_id' => Guard::deviceAccount()->id ?? 0,
                'product_id'        => $this->id,
                'num'               => 1, // 目前只能购买一个
            ]);

            return $url;
        }

        if ($this->isMoreShowProductUrl()) {
            return $this->assembleAppMoreShowUrl($attr_product_url);
        }

        return $attr_product_url;
    }

    /**
     * 是否为通过后台同步产生的有赞商品 product_url
     *
     * @return bool
     */
    private function isSyncYouzanProductUrl()
    {
        return (
            $this->youzan_item_id &&
            $this->product_url_type_id === ProductUrlType::ID_YOUZAN &&
            blank($this->attributes['product_url'])
        );
    }

    /**
     * 是否为魔售商品
     *
     * @return bool
     */
    private function isMoreShowProductUrl()
    {
        return (
            $this->product_url_type_id === ProductUrlType::ID_MORESHOW &&
            filled($this->attributes['product_url'])
        );
    }

    /**
     * @used-by filterOnShelf - 过滤上架的商品 （非删除 在售）
     *
     * @param Builder $builder
     * @return Builder
     */
    public function scopeFilterOnShelf(Builder $builder)
    {
        return $builder->where([
            $this->qualifyColumn('is_sale')    => Product::IS_SALE,
            $this->qualifyColumn('is_deleted') => Product::IS_NO_DELETE,
        ]);
    }

    /**
     * @used-by filterStoreMixCustomer() - 过滤显示 店铺并且混合显示商家的商品
     *
     * @param Builder $query
     * @param Store $store
     *
     * @return Builder
     */
    public function scopeFilterStoreMixCustomer(Builder $query, Store $store)
    {
        $store_id = $store->getKey();
        $customer_id = $store->customer_id;

        return $query
            ->where($this->qualifyColumn('customer_id'), $customer_id)
            ->whereIn($this->qualifyColumn('store_id'), [$store_id, self::NO_STORE_ID]);
    }

    /**
     * @used-by filterStorePivotCustomerProductOnShelf() - 过滤显示 店铺显示商家的商品中，上架的产品
     *
     * @param Builder $query
     *
     * @param $store_id
     * @return Builder
     */
    public function scopeFilterStorePivotCustomerProductOnShelf(Builder $query, $store_id)
    {
        return $query
            /**
             * @uses storePivotCustomerProductStores
             *
             * 使用 whereDoesntHave 是因为店铺默认显示商家商品， pvt 表没有数据，店铺操作了商家商品后 pvt 表才有数据
             */
            ->whereDoesntHave('storePivotCustomerProductStores', function (Builder $query) use($store_id){
                $pvt = new StorePivotCustomerProduct();

                // 注意这里是双重否定， 没有 不在售或被删除 的产品，
                // 需要使用 orWhere 表示 is_sale 不是 1 或 is_deleted 是 1 都是应下架，不显示
                $query->where($pvt->qualifyColumn('store_id'), $store_id)
                    ->where($pvt->qualifyColumn('is_sale'), self::IS_NO_SALE);
//                    ->orWhere($pvt->qualifyColumn('is_deleted'), self::IS_DELETE); // 这个已经不用了，只用is_sale
            });
    }

    /**
     * @used-by filterBodyPosition() - 筛选满足部位的商品
     *
     * @param Builder $query
     * @param $body_position_id
     * @return mixed|Builder
     */
    public function scopeFilterBodyPosition(Builder $query, ?int $body_position_id)
    {
        return $query->when(isset($body_position_id), function (Builder $query) use ($body_position_id) {
            $query->whereHas('bodyPositions', function (Builder $query) use ($body_position_id) {
                $query->whereIn('body_position_id', [$body_position_id, BodyPosition::COMMON]);
            });
        });
    }

    /**
     * @used-by filterSex() - 通过商品的性别，查找对应的商品
     *
     * @param Builder $query
     * @param $sex
     * @return $this|Builder
     */
    public function scopeFilterSex(Builder $query, ?int $sex)
    {
        if (is_null($sex) || $sex === 0) return $query;

        return $query->whereIn('sex', [$sex, 0]);
    }

    /**
     * @used-by $sell_price
     *
     * @return string
     */
    public function getSellPriceAttribute(): string
    {
        return sprintf('%.2f', $this->attributes['sell_price']);
    }

    /**
     * @used-by $market_price
     *
     * @return string
     */
    public function getMarketPriceAttribute(): string
    {
        return sprintf('%.2f', $this->attributes['market_price']);
    }

    /**
     * @param array $store_dev_acnt_ids
     * @return array
     */
    public static function getHasTryOnProductIds(array $store_dev_acnt_ids): array
    {
        $product_ids = DB::table('records')
            ->where([
                ['record_type_id', RecordType::PRODUCT_TRYON],
                ['objectable_type', class_basename(Product::class)],
                ['recorderable_type', class_basename(DeviceAccount::class)],
            ])
            ->whereIn('recorderable_id', $store_dev_acnt_ids)
            ->distinct()
            ->pluck('objectable_id')
            ->all();

        return $product_ids;
    }

    /**
     * @param int $amount
     * @param array $extra
     * @return int
     */
    public function incrementRefCount(int $amount = 1, array $extra = []): int
    {
        return $this->increment('ref_count', $amount, $extra);
    }

    /**
     * @used-by filterCustomersOwn - 仅过滤 商家IDs 自有商品（排除店铺的）
     *
     * @param Builder $query
     * @param array $customer_ids
     *
     * @return Builder
     */
    public function scopeFilterCustomersOwn(Builder $query, array $customer_ids)
    {
        return $query
            ->whereIn($this->qualifyColumn('customer_id'), $customer_ids)
            ->where($this->qualifyColumn('store_id'), self::STORE_ID_CUSTOMER);
    }

    /**
     * @used-by filterCategoryHasAsset - 过滤筛选商品的类别是否关联素材
     *
     * @param Builder $query
     * @param int $has_asset
     *
     * @return void
     */
    public function scopeFilterCategoryHasAsset(Builder $query, ?int $has_asset): void
    {
        if (!is_null($has_asset)) {
            $query->whereIn(
                $this->qualifyColumn('category_id'),
                Category::hasAssetIds($has_asset)
            );
        }
    }

    /**
     * @param Customer $customer
     *
     * @return string
     */
    public function getCopiedSpuCode(Customer $customer): string
    {
        $find = (new Product)
            ->where([
                ['spu_code', $this->spu_code],
                ['customer_id', $customer->id],
            ])
            ->first();

        if ($find) {
            return $find->spu_code;
        } else {
            $copied_counts = $customer->products()->where('src_product_id', $this->id)->count();

            if ($copied_counts) {
                return $this->spu_code . "($copied_counts)";
            } else {
                return $this->spu_code. "(1)";
            }
        }

    }

    /**
     * @param $product_url
     *
     * @return \Illuminate\Contracts\Routing\UrlGenerator|string
     */
    private function assembleAppMoreShowUrl($product_url)
    {
        $query = parse_url($product_url, PHP_URL_QUERY);
        parse_str($query, $query_arr);

        if (empty($query_arr['product_id'])) {
            return $product_url;
        }

        $url = url('/more-show?product_id=' . $query_arr['product_id']);

        return $url;
    }
}
